#include <arpa/inet.h>
#include <cstdint>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <string>
#include "xncc_capture.h"

static std::string ip_num_to_str(const uint32_t ip, const uint16_t port)
{
    struct in_addr addr;
    addr.s_addr = ip;
    return std::string(inet_ntoa(addr)) + ":" + std::to_string(port);
}

static void process_msg(const char* data)
{
    const struct iphdr* ip_hdr         = reinterpret_cast<const struct iphdr*>(data + sizeof(ethhdr));
    const uint16_t      ip_hdr_len     = static_cast<uint16_t>(ip_hdr->ihl << 2);
    const uint8_t       protocol       = ip_hdr->protocol;
    const uint16_t      ip_payload_len = ntohs(ip_hdr->tot_len) - ip_hdr_len;
    const uint16_t      offset         = (ntohs(ip_hdr->frag_off) & 0x1ffffU) * 8U;
    std::string         protocol_name  = "unkown";
    std::string         src_addr;
    std::string         des_addr;
    if (protocol == IPPROTO_TCP) {
        protocol_name                = "tcp";
        const struct tcphdr* tcp_hdr = reinterpret_cast<const struct tcphdr*>(data + sizeof(ethhdr) + ip_hdr_len);
        src_addr                     = ip_num_to_str((ip_hdr->saddr), ntohs(tcp_hdr->th_sport));
        des_addr                     = ip_num_to_str((ip_hdr->daddr), ntohs(tcp_hdr->th_dport));
    }
    else if (protocol == IPPROTO_UDP) {
        protocol_name                = "udp";
        const struct udphdr* udp_hdr = reinterpret_cast<const struct udphdr*>(data + sizeof(ethhdr) + ip_hdr_len);
        src_addr                     = ip_num_to_str((ip_hdr->saddr), ntohs(udp_hdr->source));
        des_addr                     = ip_num_to_str((ip_hdr->daddr), ntohs(udp_hdr->dest));
    }
    if (offset == 0) {
        printf("%s: %s->%s  :ip_hdr_len:%d ip_payload_len:%d\n", protocol_name.c_str(), src_addr.c_str(),
               des_addr.c_str(), ip_hdr_len, ip_payload_len);
    }
    else {
        printf("%s: ip_hdr_len:%d ip_payload_len:%d\n", protocol_name.c_str(), ip_hdr_len, ip_payload_len);
    }
}
int main(int argc, const char** argv)
{
    xncc_capture cap_pbj("ens160");
    cap_pbj.set_callback(process_msg);

    bool ret = cap_pbj.open();
    if (ret) {
        cap_pbj.receive();
    }
    else {
        printf("device open failed\n");
    }

    return 0;
}